package cn.zwx.remoting.netty;

import cn.zwx.constant.FeignRpcCommonConstant;
import cn.zwx.util.BeanFactoryUtils;
import cn.zwx.util.DefaultThreadFactory;
import cn.zwx.util.DelegatedScheduledExecutorServiceImpl;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.openfeign.FeignClient;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author 105286
 */
public class NettyClientRunner implements CommandLineRunner {

    @Resource
    private NettyRemotingClient client;

    @Resource
    private DiscoveryClient discoveryClient;

    private static ScheduledExecutorService executor =
            new DelegatedScheduledExecutorServiceImpl(new ScheduledThreadPoolExecutor(1,new DefaultThreadFactory("NettyClientRunner#remoteConnection")));

    private Set<String> serverLocalCacheSet = new HashSet<>();

    @Value("${server.port}")
    private int port;

    @Value("${spring.application.name}")
    private String serviceName;

    @Override
    public void run(String... args) {
        // init client start
        client.start();
        // init need serverList connects
        initServerList();
        // loopCheckAndLoadRemoteConnection
        loopCheckAndLoadRemoteConnection();
    }

    /**
     *  创建连接并且定期check连接 剔除无效连接
     * <code>
     *     1.去服务注册列表拿到 serviceList
     *     2.根据serviceList 拿到实例信息
     *     3.如果没有创建连接 则去创建连接
     *     4.如果连接失效进行剔除
     * </code>
     * @return void
     * @author zhangwenxue
     * @createTime 2022/6/20 15:52
     **/
    private void loopCheckAndLoadRemoteConnection() {
        executor.scheduleWithFixedDelay(() -> {
            //1.获取并创建新的链接
            List<String> services = discoveryClient.getServices();

            List<String> keys = new LinkedList<>();
            if (null != services) {
                for (String service : services) {
                    if (serviceName.equals(service) || !serverLocalCacheSet.contains(service)){
                        continue;
                    }
                    List<ServiceInstance> instances = discoveryClient.getInstances(service);
//                    System.out.println(JSON.toJSONString(instances));
                    if (null != instances) {
                        for (ServiceInstance instance : instances) {
                            String ip = instance.getHost();
                            int port = instance.getPort() + FeignRpcCommonConstant.STEP;
                            keys.add(createKeyWithIpAndPort(ip, port));
                        }
                    }
                }
            }
            //1.创建最近启动的服务
            for (String key : keys) {
                if (!client.channelActive(key)) {
                    String[] ipAndPorts = key.split("_");
                    client.connect(ipAndPorts[0], Integer.parseInt(ipAndPorts[1]));
                }
            }

            //2.踢去掉失效的链接
            client.removeUnActiveChannel();

        }, 0, 5, TimeUnit.SECONDS);
    }

    /**
     * createKeyWithIPAndPort
     * @param ip ip
     * @param port 端口
     * @return java.lang.String
     * @author zhangwenxue
     * @createTime 2022/6/20 16:00
     **/
    private static String createKeyWithIpAndPort(String ip,int port){
        return ip+"_"+port;
    }

    /**
     *
     * 获取当前服务项目的需要创建连接的数量
     * @return void
     * @author zhangwenxue
     * @createTime 2022/6/20 15:31
     **/
    private void initServerList() {
        String[] beanNamesForAnnotation = BeanFactoryUtils.getBeanFactory().getBeanNamesForAnnotation(FeignClient.class);
        if (beanNamesForAnnotation.length<=0){
            return;
        }
        for (String beanName : beanNamesForAnnotation) {
            Object target = BeanFactoryUtils.getBeanFactory().getBean(beanName);
            Class<?>[] interfaces = AopUtils.getTargetClass(target).getInterfaces();
            if(interfaces.length == 0) {
                continue;
            }
            for(Class interfaceTemp:interfaces){
                FeignClient annotation = (FeignClient) interfaceTemp.getAnnotation(FeignClient.class);
                if (annotation == null) {
                    continue;
                }
                String value = annotation.value();
                if (StringUtils.isBlank(value)){
                     value = annotation.name();
                }
                if (StringUtils.isNotBlank(value)){
                    serverLocalCacheSet.add(value);
                }

            }
        }
    }
}
